<?php

namespace yifang\lib;

use yifang\App;
use Closure;
use yifang\route\rule\RuleGroup;
use RuntimeException;
use yifang\route\dispatch\Callback;
use yifang\route\dispatch\Url as UrlDispatch;
use yifang\response\Response;
use yifang\route\rule\RuleName;
use yifang\route\rule\RuleItem;
use Exception;

class Route
{
    /**
     * REST定义
     * @var array
     */
    protected $rest = [
        'index'  => ['get', '', 'index'],
        'create' => ['get', '/create', 'create'],
        'edit'   => ['get', '/<id>/edit', 'edit'],
        'read'   => ['get', '/<id>', 'read'],
        'save'   => ['post', '', 'save'],
        'update' => ['put', '/<id>', 'update'],
        'delete' => ['delete', '/<id>', 'delete'],
    ];

    /**
     * 配置参数
     * @var array
     */
    protected $config = [
        // pathinfo分隔符
        'pathinfo_depr'         => '/',
        // 是否开启路由延迟解析
        'url_lazy_route'        => false,
        // 是否强制使用路由
        'url_route_must'        => false,
        // 合并路由规则
        'route_rule_merge'      => false,
        // 路由是否完全匹配
        'route_complete_match'  => false,
        // 去除斜杠
        'remove_slash'          => false,
        // 使用注解路由
        'route_annotation'      => false,
        // 默认的路由变量规则
        'default_route_pattern' => '[\w\.]+',
        // URL伪静态后缀
        'url_html_suffix'       => 'html',
        // 访问控制器层名称
        'controller_layer'      => 'controller',
        // 空控制器名
        'empty_controller'      => 'Error',
        // 是否使用控制器后缀
        'controller_suffix'     => false,
        // 默认控制器名
        'default_controller'    => 'Index',
        // 默认操作名
        'default_action'        => 'index',
        // 操作方法后缀
        'action_suffix'         => '',
        // 非路由变量是否使用普通参数方式（用于URL生成）
        'url_common_param'      => true,
    ];

    /**
     * 当前应用
     * @var App
     */
    protected $app;

    /**
     * 请求对象
     * @var Request
     */
    protected $request;

    /**
     * 当前HOST
     * @var string
     */
    protected $host;

    /**
     * 当前分组对象
     * @var RuleGroup
     */
    protected $group;

    /**
     * 路由绑定
     * @var array
     */
    protected $bind = [];

    /**
     * 域名对象
     */
    protected $domains = [];

    /**
     * 跨域路由规则
     */
    protected $cross;

    /**
     * 路由是否延迟解析
     * @var bool
     */
    protected $lazy = false;

    /**
     * 路由是否测试模式
     * @var bool
     */
    protected $isTest = false;

    /**
     * （分组）路由规则是否合并解析
     * @var bool
     */
    protected $mergeRuleRegex = false;

    /**
     * @var RuleName
     */
    protected $ruleName;

    /**
     * 是否去除URL最后的斜线
     * @var bool
     */
    protected $removeSlash = false;

    public function __construct(App $app)
    {
        $this->app      = $app;
        $this->ruleName = new RuleName();
        $this->setDefaultGroup();
        $routeRuntimePath=$this->app->getRuntimePath() . 'route.php';
        if (is_file($routeRuntimePath)) {
            // 读取路由映射文件
            $this->import(include $routeRuntimePath);
        }
        $this->config = array_merge($this->config, $this->app->config->get('route'));
        $this->init();
    }

    /**
     * 读取路由标识
     */
    public function getName($name = null, $domain = null,$method = '*')
    {
        return $this->ruleName->getName($name, $domain, $method);
    }

    /**
     * 批量导入路由标识
     */
    public function import(array $name)
    {
        $this->ruleName->import($name);
    }

    /**
     * 注册路由标识
     */
    public function setName($name, RuleItem $ruleItem, $first = false)
    {
        $this->ruleName->setName($name, $ruleItem, $first);
    }

    /**
     * 保存路由规则
     */
    public function setRule($rule, RuleItem $ruleItem = null)
    {
        $this->ruleName->setRule($rule, $ruleItem);
    }

    /**
     * 读取路由
     */
    public function getRule($rule)
    {
        return $this->ruleName->getRule($rule);
    }

    /**
     * 读取路由列表
     */
    public function getRuleList($method="",$route="")
    {
        return $this->ruleName->getRuleList($method,$route);
    }

    /**
     * 注册未匹配路由规则后的处理
     */
    public function miss($route, $method = '*')
    {
        return $this->group->miss($route, $method);
    }

    /**
     * 清空路由规则
     */
    public function clear()
    {
        $this->ruleName->clear();
        if ($this->group) {
            $this->group->clear();
        }
    }

    /**
     * 默认分组
     */
    public function setDefaultGroup(){
        $this->group = new RuleGroup($this);
    }

    /**
     * 初始化路由
     */
    protected function init()
    {
        if (!empty($this->config['middleware'])) {
            $this->app->middleware->import($this->config['middleware'], 'route');
        }
        $this->lazy($this->config['url_lazy_route']);
        $this->mergeRuleRegex = $this->config['route_rule_merge'];
        $this->removeSlash    = $this->config['remove_slash'];
        $this->group->removeSlash($this->removeSlash);
    }

    /**
     * 设置路由域名及分组（包括资源路由）是否延迟解析
     */
    public function lazy($lazy = true)
    {
        $this->lazy = $lazy;
        return $this;
    }

    /**
     * 路由调度
     */
    public function dispatch(Request $request, $withRoute = true)
    {
        $this->request = $request;
        $this->host    = $this->request->host(true);
        if ($withRoute) {
            //加载路由
            if ($withRoute instanceof Closure) {
                $withRoute();
            }
            $dispatch = $this->check();
        } else {
            $dispatch = $this->url($this->path());
        }
        $dispatch->init($this->app);
        return $this->app->middleware->pipeline('route')->send($request)->then(function () use ($dispatch) {
            return $dispatch->run();
        });
    }

    /**
     * 检测URL路由
     */
    public function check()
    {
        // 自动检测域名路由
        $url = str_replace($this->config['pathinfo_depr'], '|', $this->path());
        $completeMatch = $this->config['route_complete_match'];
        $result = $this->group->check($this->request, $url, $completeMatch);
        if (false !== $result) {
            return $result;
        } elseif ($this->config['url_route_must']) {
            throw new Exception("路由没找到",404);
        }
        return $this->url($url);
    }

    /**
     * 注册变量规则
     */
    public function pattern(array $pattern)
    {
        $this->group->pattern($pattern);
        return $this;
    }

    /**
     * 获取当前请求URL的pathinfo信息(不含URL后缀)
     */
    protected function path()
    {
        $suffix   = $this->config['url_html_suffix'];
        $pathinfo = $this->request->pathinfo();
        if (false === $suffix) {
            // 禁止伪静态访问
            $path = $pathinfo;
        } elseif ($suffix) {
            // 去除正常的URL后缀
            $path = preg_replace('/\.(' . ltrim($suffix, '.') . ')$/i', '', $pathinfo);
        } else {
            // 允许任何后缀访问
            $path = preg_replace('/\.' . $this->request->ext() . '$/i', '', $pathinfo);
        }
        return $path;
    }

    /**
     * 默认URL解析
     */
    public function url($url)
    {
        if ($this->request->method() == 'OPTIONS') {
            // 自动响应options请求
            return new Callback($this->request, $this->group, function () {
                return response('', 204,['Allow' => 'GET, POST, PUT, DELETE'],'html');
            });
        }
        return new UrlDispatch($this->request, $this->group, $url);
    }

    /**
     * 获取配置
     */
    public function config($name = null)
    {
        if (is_null($name)) {
            return $this->config;
        }
        return $this->config[$name] ?? null;
    }

    /**
     * 获取RuleName对象
     */
    public function getRuleName()
    {
        return $this->ruleName;
    }

    /**
     * 设置当前分组
     */
    public function setGroup(RuleGroup $group)
    {
        $this->group = $group;
    }

    /**
     * 获取指定标识的路由分组 不指定则获取当前分组
     */
    public function getGroup($name = null)
    {
        return $name ? $this->ruleName->getGroup($name) : $this->group;
    }

    /**
     * 注册路由分组
     */
    public function group($name, $route = null,$middleware=null,$middleware_args=null)
    {
        if ($name instanceof Closure) {
            $middleware_args=$middleware;
            $middleware=$route;
            $route = $name;
            $name  = '';
        }
        return (new RuleGroup($this, $this->group, $name, $route))->lazy($this->lazy)->removeSlash($this->removeSlash)->mergeRuleRegex($this->mergeRuleRegex)->middleware($middleware,$middleware_args);
    }

    /**
     * 注册路由规则
     */
    public function rule($rule, $route = null, $method = '*')
    {
        if ($route instanceof Response) {
            // 兼容之前的路由到响应对象，感觉不需要，使用场景很少，闭包就能实现
            $route = function () use ($route) {
                return $route;
            };
        }
        return $this->group->addRule($rule, $route, $method);
    }

    /**
     * 注册GET路由
     */
    public function get($rule, $route)
    {
        return $this->rule($rule, $route, 'GET');
    }

    /**
     * 注册POST路由
     */
    public function post($rule, $route)
    {
        return $this->rule($rule, $route, 'POST');
    }

    /**
     * 注册PUT路由
     */
    public function put($rule, $route)
    {
        return $this->rule($rule, $route, 'PUT');
    }

    /**
     * 注册路由
     */
    public function any($rule, $route)
    {
        return $this->rule($rule, $route, '*');
    }

    /**
     * 注册DELETE路由
     */
    public function delete($rule, $route)
    {
        return $this->rule($rule, $route, 'DELETE');
    }

    /**
     * 注册PATCH路由
     */
    public function patch($rule, $route)
    {
        return $this->rule($rule, $route, 'PATCH');
    }

    /**
     * 注册HEAD路由
     */
    public function head($rule, $route)
    {
        return $this->rule($rule, $route, 'HEAD');
    }

    /**
     * 注册OPTIONS路由
     */
    public function options($rule, $route)
    {
        return $this->rule($rule, $route, 'OPTIONS');
    }

    /**
     * 设置全局的路由分组参数
     */
    public function __call($method, $args)
    {
        return call_user_func_array([$this->group, $method], $args);
    }
}